#include <winsock2.h>  // This must be included before "windows.h"
#include <windows.h>

#include <iphlpapi.h>
#pragma comment(lib, "IPHLPAPI.lib")

#define HAVE_REMOTE    // This must be defined before "pcap.h"
#include <pcap.h>

#include <string.h>

#include "global.h"
#include "headers.h"

#include "open.h"

#define PCAP_TIMEOUT  10   // Counted in milliseconds

/* Note: could also use malloc() and free() */
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))

const char pcap_prefix[] = "rpcap://\\Device\\NPF_";

int open_device( void )
{
	// Get adapter information list using IP Helper API

	/* Declare and initialize variables */
	DWORD dwSize = 0;
	DWORD dwRetVal = 0;
	int k = 0;

	// Set the flags to pass to GetAdaptersAddresses
	ULONG flags = GAA_FLAG_INCLUDE_PREFIX;

	ULONG family = AF_UNSPEC;
	LPVOID lpMsgBuf = NULL;
	PIP_ADAPTER_ADDRESSES pAddresses = NULL;
	ULONG outBufLen = 0;

	PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
	//IP_ADAPTER_PREFIX *pPrefix = NULL;

	outBufLen = sizeof (IP_ADAPTER_ADDRESSES);
	pAddresses = (IP_ADAPTER_ADDRESSES *) MALLOC(outBufLen);

	// Make an initial call to GetAdaptersAddresses to get the 
	// size needed into the outBufLen variable
	if (GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen)
		== ERROR_BUFFER_OVERFLOW) {
		FREE(pAddresses);
		pAddresses = (IP_ADAPTER_ADDRESSES *) MALLOC(outBufLen);
	}

	if (pAddresses == NULL) {
		printf("Memory allocation failed for IP_ADAPTER_ADDRESSES struct\n");
		exit(1);
	}
	// Make a second call to GetAdapters Addresses to get the
	// actual data we want
	dwRetVal =
		GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen);

	if (dwRetVal != NO_ERROR) {
		printf("Call to GetAdaptersAddresses failed with error: %d\n",
			   dwRetVal);
		if (dwRetVal == ERROR_NO_DATA)
			printf("\tNo addresses were found for the requested parameters\n");
		else {
			if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwRetVal, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),   // Default language
							  (LPTSTR) & lpMsgBuf, 0, NULL)) {
				printf("\tError: %s", lpMsgBuf);
				LocalFree(lpMsgBuf);
				FREE(pAddresses);
				return -1;
			}
		}
		return -1;
	}

	// If successful, continue to open pcap devices

	// Open pcap devices

	pcap_if_t *alldevs;
	pcap_if_t *d;
	int inum4, inum6;
	int i=0, j=0;
	char errbuf[PCAP_ERRBUF_SIZE];

	/* Retrieve the device list on the local machine */
	if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
	{
		fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
		return -1;
	}

	/* Print the list */
	for(d=alldevs; d; d=d->next)
	{
		printf("%d. %s", ++i, d->name);
		if (d->description)
			printf(" (%s)\n", d->description);
		else
			printf(" (No description available)\n");
	}

	if(i==0)
	{
		printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
		return -1;
	}

	// Select v4 device
	printf("Enter the interface number (1-%d) for your v4 device:",i);
	scanf_s("%d", &inum4);

	if(inum4 < 1 || inum4 > i)
	{
		printf("\nInterface number out of range.\n");
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return -1;
	}

	/* Jump to the selected adapter */
	for(d=alldevs, j=0; j< inum4-1 ;d=d->next, j++);

	/* Open the device */
	if ( (v4Handle= pcap_open(d->name,          // name of the device
							  65536,            // portion of the packet to capture. 
												// 65536 guarantees that the whole packet will be captured on all the link layers
							  PCAP_OPENFLAG_PROMISCUOUS,    // promiscuous mode
							  PCAP_TIMEOUT,     // read timeout
							  NULL,             // authentication on the remote machine
							  v4Errbuf          // error buffer
							  ) ) == NULL)
	{
		fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return -1;
	}

	// Get MAC address of this adaptor
	pCurrAddresses = pAddresses;
	while (pCurrAddresses) {
		if( strcmp( pCurrAddresses->AdapterName, d->name + strlen(pcap_prefix) ) == 0 ) {
			if (pCurrAddresses->PhysicalAddressLength == 6) {
				for (k = 0; k < 6; k++) {
					v4LocalMAC[k] = (u_char)(pCurrAddresses->PhysicalAddress[k]);
				}
			}
			else
				printf("v4 device: Mal-format hardware address!\n");

			break;
		}
		pCurrAddresses = pCurrAddresses->Next;
	}

	printf("Listening on %s...\n", d->description);

	// Select v6 device
	printf("Enter the interface number (1-%d) for your v6 device:",i);
	scanf_s("%d", &inum6);

	if(inum6 < 1 || inum6 > i)
	{
		printf("\nInterface number out of range.\n");
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return -1;
	}

	if(inum6 == inum4 )
	{
		printf("\nIVI4 device and IVI6 device cannot be the same.\n");
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return -1;
	}

	/* Jump to the selected adapter */
	for(d=alldevs, j=0; j< inum6-1 ;d=d->next, j++);

	/* Open the device */
	if ( (v6Handle= pcap_open(d->name,          // name of the device
							  65536,            // portion of the packet to capture. 
												// 65536 guarantees that the whole packet will be captured on all the link layers
							  PCAP_OPENFLAG_PROMISCUOUS,    // promiscuous mode
							  PCAP_TIMEOUT,     // read timeout
							  NULL,             // authentication on the remote machine
							  v6Errbuf          // error buffer
							  ) ) == NULL)
	{
		fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return -1;
	}

	// Get MAC address of this adaptor
	pCurrAddresses = pAddresses;
	while (pCurrAddresses) {
		if( strcmp( pCurrAddresses->AdapterName, d->name + strlen(pcap_prefix) ) == 0 ) {
			if (pCurrAddresses->PhysicalAddressLength == 6) {
				for (k = 0; k < 6; k++) {
					v6LocalMAC[k] = (u_char)(pCurrAddresses->PhysicalAddress[k]);
				}
			}
			else
				printf("v6 device: Mal-format hardware address!\n");

			break;
		}
		pCurrAddresses = pCurrAddresses->Next;
	}

	printf("Listening on %s...\n", d->description);

	/* At this point, we don't need any more the device list. Free it */
	pcap_freealldevs(alldevs);

	// Free adaptor information list
	FREE(pAddresses);

	return 0;
}
